1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.hiprenderer.renderer; 12 public import hip.hiprenderer.config; 13 public import hip.hiprenderer.shader; 14 public import hip.hiprenderer.vertex; 15 public import hip.hiprenderer.framebuffer; 16 public import hip.hiprenderer.viewport; 17 public import hip.api.renderer.texture; 18 public import hip.api.renderer.operations; 19 public import hip.api.graphics.color; 20 public import hip.hiprenderer.shader.shadervar; 21 import hip.windowing.window; 22 import hip.math.rect; 23 import hip.error.handler; 24 import hip.console.log; 25 26 public import hip.hiprenderer.backend.gl.glrenderer; 27 28 version(Windows) 29 { 30 import hip.hiprenderer.backend.d3d.d3drenderer; 31 import hip.hiprenderer.backend.d3d.d3dtexture; 32 } 33 version(AppleOS) 34 { 35 import hip.hiprenderer.backend.metal.mtlrenderer; 36 import hip.hiprenderer.backend.metal.mtltexture; 37 } 38 39 version(dll){} else version = RendererConfigFile; 40 import hip.hiprenderer.backend.gl.gltexture; 41 42 ///Could later be moved to windowing 43 enum HipWindowMode 44 { 45 WINDOWED, 46 FULLSCREEN, 47 BORDERLESS_FULLSCREEN 48 } 49 50 ///Which API is being used 51 enum HipRendererType 52 { 53 GL3, 54 D3D11, 55 METAL, 56 NONE 57 } 58 59 /// Primitive which the renderer will use 60 enum HipRendererMode 61 { 62 POINT, 63 LINE, 64 LINE_STRIP, 65 TRIANGLES, 66 TRIANGLE_STRIP 67 } 68 69 70 71 72 //////////////////////////////////////////Metadata////////////////////////////////////////// 73 74 //Shaders 75 enum HipShaderInputLayout; 76 /** 77 * Use this special UDA to say this type is only for accumulating stride and thus should not 78 * be defined on shader 79 */ 80 enum HipShaderInputPadding; 81 /** 82 * Declares that the struct is as VertexUniform block. 83 */ 84 struct HipShaderVertexUniform 85 { 86 /** 87 * This name is the base uniform name accessed when dealing with HLSL Api. 88 * i.e: Constant Buffer block name 89 */ 90 string name; 91 } 92 /** 93 * Declares that the struct is as FragmentUniform block. 94 */ 95 struct HipShaderFragmentUniform 96 { 97 /** 98 * This name is the base uniform name accessed when dealing with HLSL Api. 99 * i.e: Constant Buffer block name 100 */ 101 string name; 102 } 103 104 /** 105 * Minimal interface for another API implementation 106 */ 107 interface IHipRendererImpl 108 { 109 public bool init(HipWindow window); 110 version(dll){public bool initExternal();} 111 public bool isRowMajor(); 112 void setErrorCheckingEnabled(bool enable = true); 113 public Shader createShader(); 114 public ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length); 115 public IHipFrameBuffer createFrameBuffer(int width, int height); 116 public IHipVertexArrayImpl createVertexArray(); 117 public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage); 118 public IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage); 119 public IHipTexture createTexture(); 120 public int queryMaxSupportedPixelShaderTextures(); 121 public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255); 122 public void setViewport(Viewport v); 123 public bool setWindowMode(HipWindowMode mode); 124 public void setDepthTestingEnabled(bool); 125 public void setDepthTestingFunction(HipDepthTestingFunction); 126 public void setStencilTestingEnabled(bool); 127 public void setStencilTestingMask(uint mask); 128 public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a); 129 ///When pass func evaluates to true, then it is said to be passed 130 public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask); 131 public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass); 132 public bool hasErrorOccurred(out string err, string line = __FILE__, size_t line =__LINE__); 133 public void begin(); 134 public void setRendererMode(HipRendererMode mode); 135 public void drawIndexed(index_t count, uint offset = 0); 136 public void drawVertices(index_t count, uint offset = 0); 137 public void end(); 138 public void clear(); 139 public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255); 140 public void dispose(); 141 } 142 143 private struct HipRendererResources 144 { 145 IHipTexture[] textures; 146 Shader[] shaders; 147 IHipVertexArrayImpl[] vertexArrays; 148 IHipVertexBufferImpl[] vertexBuffers; 149 IHipIndexBufferImpl[] indexBuffers; 150 } 151 152 class HipRenderer 153 { 154 static struct Statistics 155 { 156 ulong drawCalls; 157 ulong renderFrames; 158 } 159 __gshared 160 { 161 protected Viewport currentViewport; 162 protected Viewport mainViewport; 163 protected IHipRendererImpl rendererImpl; 164 protected HipRendererMode rendererMode; 165 protected Statistics stats; 166 public HipWindow window = null; 167 public Shader currentShader; 168 package HipRendererType rendererType = HipRendererType.NONE; 169 170 public uint width, height; 171 protected HipRendererConfig currentConfig; 172 173 protected HipRendererResources res; 174 protected bool depthTestingEnabled; 175 protected HipDepthTestingFunction currentDepthTestFunction; 176 } 177 178 version(RendererConfigFile) 179 public static bool initialize (string confData, string confPath) 180 { 181 import hip.data.ini; 182 IniFile ini = IniFile.parse(confData, confPath); 183 HipRendererConfig cfg; 184 if(ini.configFound && ini.noError) 185 { 186 cfg.bufferingCount = ini.tryGet!ubyte("buffering.count", 2); 187 cfg.multisamplingLevel = ini.tryGet!ubyte("multisampling.level", 0); 188 cfg.fullscreen = ini.tryGet("screen.fullscreen", false); 189 cfg.vsync = ini.tryGet("vsync.on", true); 190 191 int width = ini.tryGet("screen.width", 1280); 192 int height = ini.tryGet("screen.height", 720); 193 string renderer = ini.tryGet("screen.renderer", "GL3"); 194 195 switch(renderer) 196 { 197 case "GL3": 198 version(OpenGL) 199 { 200 rendererType = HipRendererType.GL3; 201 return initialize(new Hip_GL3Renderer(), &cfg, width, height); 202 } 203 else version(DirectX) 204 { 205 logln("OpenGL wasn't included in this build, using Direct3D"); 206 goto case "D3D11"; 207 } 208 else version(AppleOS) 209 { 210 logln("OpenGL wasn't included in this build, using Metal"); 211 goto case "METAL"; 212 } 213 case "D3D11": 214 version(DirectX) 215 { 216 rendererType = HipRendererType.D3D11; 217 return initialize(new Hip_D3D11_Renderer(), &cfg, width, height); 218 } 219 else version(OpenGL) 220 { 221 logln("Direct3D wasn't included in this build, using OpenGL 3"); 222 goto case "GL3"; 223 } 224 else version(AppleOS) 225 { 226 logln("Direct3D wasn't included in this build, using Metal"); 227 goto case "METAL"; 228 } 229 case "METAL": 230 version(AppleOS) 231 { 232 rendererType = HipRendererType.METAL; 233 return initialize(new HipMTLRenderer(), &cfg, width, height); 234 } 235 else version(OpenGL) 236 { 237 logln("Metal wasn't included in this build, using OpenGL 3"); 238 goto case "GL3"; 239 } 240 else version(DirectX) 241 { 242 logln("Metal wasn't included in this build, using Direct3D"); 243 goto case "D3D11"; 244 } 245 default: 246 logln("Invalid renderer?" , renderer, " ' oh my freakin goodness"); 247 ErrorHandler.showErrorMessage("Invalid renderer '"~renderer~"'", 248 ` 249 Available renderers: 250 GL3 251 D3D11 252 METAL 253 Starting with GL3 254 `); 255 goto case "GL3"; 256 } 257 } 258 else 259 { 260 string defaultRenderer = "OpenGL3"; 261 version(AppleOS) defaultRenderer = "Metal"; 262 if(!ini.configFound) 263 logln("No renderer.conf found"); 264 if(!ini.noError) 265 { 266 logln("Renderer.conf parsing error"); 267 rawerror(ini.errors); 268 } 269 hiplog("Defaulting renderer to "~defaultRenderer); 270 } 271 return initialize(getRendererFromVersion(rendererType), &cfg, 1280, 720); 272 } 273 274 public static Statistics getStatistics(){return stats;} 275 private static IHipRendererImpl getRendererFromVersion(out HipRendererType type) 276 { 277 version(OpenGL) 278 { 279 type = HipRendererType.GL3; 280 return new Hip_GL3Renderer(); 281 } 282 else version(DirectX) 283 { 284 type = HipRendererType.D3D11; 285 return new Hip_D3D11_Renderer(); 286 } 287 else version(AppleOS) 288 { 289 type = HipRendererType.METAL; 290 return new HipMTLRenderer(); 291 } 292 else 293 { 294 type = HipRendererType.NONE; 295 return null; 296 } 297 } 298 version(dll) private static IHipRendererImpl getRenderer(ref HipRendererType type) 299 { 300 final switch(type) 301 { 302 case HipRendererType.D3D11: 303 version(DirectX) return new Hip_D3D11_Renderer(); 304 else return getRendererFromVersion(type); 305 case HipRendererType.GL3: 306 version(OpenGL) return new Hip_GL3Renderer(); 307 else return getRendererFromVersion(type); 308 case HipRendererType.METAL: 309 version(AppleOS) return new HipMTLRenderer(); 310 else return getRendererFromVersion(type); 311 case HipRendererType.NONE: 312 return null; 313 } 314 } 315 316 version(dll) public static bool initExternal(HipRendererType type, int windowWidth = -1, int windowHeight = -1) 317 { 318 rendererImpl = getRenderer(type); 319 HipRenderer.rendererType = type; 320 logln("Initialized renderer.: ", rendererType, " renderer? ", rendererImpl !is null); 321 bool ret = rendererImpl.initExternal(); 322 if(!ret) 323 ErrorHandler.showErrorMessage("Error Initializing Renderer", "Renderer could not initialize externally"); 324 325 if(windowWidth == -1) 326 windowWidth = 1920; 327 if(windowHeight == -1) 328 windowHeight = 1080; 329 330 window = createWindow(windowWidth, windowHeight); 331 HipRenderer.width = window.width; 332 HipRenderer.height = window.height; 333 afterInit(); 334 return ret; 335 } 336 private static afterInit() 337 { 338 import hip.config.opts; 339 mainViewport = new Viewport(0,0, window.width, window.height); 340 setViewport(mainViewport); 341 setColor(); 342 HipRenderer.setRendererMode(HipRendererMode.TRIANGLES); 343 } 344 345 private static HipWindow createWindow(uint width, uint height) 346 { 347 HipWindow wnd = new HipWindow(width, height, HipWindowFlags.DEFAULT); 348 version(Android){} 349 else wnd.start(); 350 return wnd; 351 } 352 353 public static bool initialize (IHipRendererImpl impl, HipRendererConfig* config, uint width, uint height) 354 { 355 ErrorHandler.startListeningForErrors("Renderer initialization"); 356 if(config != null) 357 currentConfig = *config; 358 currentConfig.logConfiguration(); 359 rendererImpl = impl; 360 window = createWindow(width, height); 361 ErrorHandler.assertErrorMessage(window !is null, "Error creating window", "Could not create Window"); 362 rendererImpl.init(window); 363 window.setVSyncActive(currentConfig.vsync); 364 window.setFullscreen(currentConfig.fullscreen); 365 window.show(); 366 foreach(err; window.errors) 367 loglnError(err); 368 369 setWindowSize(width, height); 370 afterInit(); 371 return ErrorHandler.stopListeningForErrors(); 372 } 373 public static void setWindowSize(int width, int height) 374 { 375 assert(width > 0 && height > 0, "Window width and height must be greater than 0"); 376 logln("Changing window size to ", [width, height]); 377 window.setSize(cast(uint)width, cast(uint)height); 378 HipRenderer.width = width; 379 HipRenderer.height = height; 380 } 381 public static HipRendererType getRendererType(){return rendererType;} 382 public static HipRendererConfig getCurrentConfig(){return currentConfig;} 383 public static int getMaxSupportedShaderTextures(){return rendererImpl.queryMaxSupportedPixelShaderTextures();} 384 385 386 private static IHipTexture _getTextureImplementation() 387 { 388 return rendererImpl.createTexture(); 389 } 390 public static IHipTexture getTextureImplementation() 391 { 392 res.textures~= _getTextureImplementation(); 393 return res.textures[$-1]; 394 } 395 396 public static void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 397 { 398 rendererImpl.setColor(r,g,b,a); 399 } 400 401 public static Viewport getCurrentViewport(){return currentViewport;} 402 public static void setViewport(Viewport v) 403 { 404 this.currentViewport = v; 405 v.updateForWindowSize(width, height); 406 rendererImpl.setViewport(v); 407 } 408 409 public static void reinitialize() 410 { 411 version(Android) 412 { 413 foreach(tex; res.textures) 414 { 415 (cast(Hip_GL3_Texture)tex).reload(); 416 } 417 foreach(shader; res.shaders) 418 { 419 shader.reload(); 420 } 421 } 422 } 423 424 public static void setCamera() 425 { 426 427 } 428 /** 429 * Fixes the matrix order based on the config and renderer. 430 * If the renderer is column and the config is row, it will tranpose 431 */ 432 public static T getMatrix(T)(ref T mat) 433 { 434 if(currentConfig.isMatrixRowMajor && !rendererImpl.isRowMajor()) 435 return mat.transpose(); 436 return mat; 437 } 438 439 private static Shader _createShader() 440 { 441 res.shaders~= rendererImpl.createShader(); 442 return res.shaders[$-1]; 443 } 444 public static ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length) 445 { 446 return rendererImpl.createShaderVar(shaderType, uniformType, varName, length); 447 } 448 449 450 public static Shader newShader(HipShaderPresets shaderPreset = HipShaderPresets.DEFAULT) 451 { 452 Shader ret = _createShader(); 453 ret.setFromPreset(shaderPreset); 454 return ret; 455 } 456 public static Shader newShader(string vertexShader, string fragmentShader) 457 { 458 Shader ret = _createShader(); 459 ret.loadShadersFromFiles(vertexShader, fragmentShader); 460 return ret; 461 } 462 public static HipFrameBuffer newFrameBuffer(int width, int height, Shader frameBufferShader = null) 463 { 464 return new HipFrameBuffer(rendererImpl.createFrameBuffer(width, height), width, height, frameBufferShader); 465 } 466 public static IHipVertexArrayImpl createVertexArray() 467 { 468 res.vertexArrays~= rendererImpl.createVertexArray(); 469 return res.vertexArrays[$-1]; 470 } 471 public static IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage) 472 { 473 res.vertexBuffers~= rendererImpl.createVertexBuffer(size, usage); 474 return res.vertexBuffers[$-1]; 475 } 476 public static IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage) 477 { 478 res.indexBuffers~= rendererImpl.createIndexBuffer(count, usage); 479 return res.indexBuffers[$-1]; 480 } 481 public static void setShader(Shader s) 482 { 483 currentShader = s; 484 s.bind(); 485 } 486 public static bool hasErrorOccurred(out string err, string file = __FILE__, size_t line =__LINE__) 487 { 488 return rendererImpl.hasErrorOccurred(err, file, line); 489 } 490 491 public static void exitOnError(string file = __FILE__, size_t line = __LINE__) 492 { 493 import core.stdc.stdlib:exit; 494 string err; 495 if(hasErrorOccurred(err, file, line)) 496 { 497 loglnError(err, file,":",line); 498 exit(-1); 499 } 500 } 501 502 public static void begin() 503 { 504 505 rendererImpl.begin(); 506 } 507 508 public static void setErrorCheckingEnabled(bool enable = true) 509 { 510 rendererImpl.setErrorCheckingEnabled(enable); 511 } 512 513 public static void setRendererMode(HipRendererMode mode) 514 { 515 rendererMode = mode; 516 rendererImpl.setRendererMode(mode); 517 } 518 public static HipRendererMode getMode(){return rendererMode;} 519 520 public static void drawIndexed(index_t count, uint offset = 0) 521 { 522 rendererImpl.drawIndexed(count, offset); 523 stats.drawCalls++; 524 } 525 public static void drawIndexed(HipRendererMode mode, index_t count, uint offset = 0) 526 { 527 HipRendererMode currMode = rendererMode; 528 if(mode != currMode) HipRenderer.setRendererMode(mode); 529 HipRenderer.drawIndexed(count, offset); 530 stats.drawCalls++; 531 if(mode != currMode) HipRenderer.setRendererMode(currMode); 532 } 533 public static void drawVertices(index_t count, uint offset = 0) 534 { 535 rendererImpl.drawVertices(count, offset); 536 } 537 public static void drawVertices(HipRendererMode mode, index_t count, uint offset = 0) 538 { 539 rendererImpl.setRendererMode(mode); 540 HipRenderer.drawVertices(count, offset); 541 } 542 543 public static void end() 544 { 545 rendererImpl.end(); 546 foreach(sh; res.shaders) sh.onRenderFrameEnd(); 547 stats.drawCalls=0; 548 stats.renderFrames++; 549 } 550 public static void clear() 551 { 552 rendererImpl.clear(); 553 stats.drawCalls++; 554 } 555 public static void clear(HipColorf color) 556 { 557 auto rgba = color.unpackRGBA; 558 rendererImpl.clear(rgba[0], rgba[1], rgba[2], rgba[3]); 559 stats.drawCalls++; 560 } 561 public static void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 562 { 563 rendererImpl.clear(r,g,b,a); 564 stats.drawCalls++; 565 } 566 static HipDepthTestingFunction getDepthTestingFunction() 567 { 568 return currentDepthTestFunction; 569 } 570 static bool isDepthTestingEnabled() 571 { 572 return depthTestingEnabled; 573 } 574 static void setDepthTestingEnabled(bool enable) 575 { 576 rendererImpl.setDepthTestingEnabled(enable); 577 } 578 static void setDepthTestingFunction(HipDepthTestingFunction fn) 579 { 580 rendererImpl.setDepthTestingFunction(fn); 581 currentDepthTestFunction = fn; 582 } 583 584 static void setStencilTestingEnabled(bool bEnable) 585 { 586 rendererImpl.setStencilTestingEnabled(bEnable); 587 } 588 static void setStencilTestingMask(uint mask) 589 { 590 rendererImpl.setStencilTestingMask(mask); 591 } 592 static void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a) 593 { 594 rendererImpl.setColorMask(r,g,b,a); 595 } 596 static void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask) 597 { 598 rendererImpl.setStencilTestingFunction(passFunc, reference, mask); 599 } 600 static void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass) 601 { 602 rendererImpl.setStencilOperation(stencilFail, depthFail, stencilAndDephPass); 603 } 604 605 public static void dispose() 606 { 607 rendererImpl.dispose(); 608 if(window !is null) 609 window.exit(); 610 window = null; 611 } 612 }